package com.tao.handler;


import com.alibaba.fastjson.JSON;
import com.tao.constants.SystemConstant;
import com.tao.dto.MessageDto;
import com.tao.entity.LoginUser;
import com.tao.enums.AppHttpCodeEnum;
import com.tao.exception.SystemException;
import com.tao.utils.JwtUtils;
import com.tao.utils.RedisCache;
import com.tao.vo.SocketResult;
import io.jsonwebtoken.Claims;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author  诸葛先生
 * 功能：Socket聊天处理器
 */
@Component
@ChannelHandler.Sharable
//这里 TextWebSocketFrame 类型，表示一个文本帧(frame)
public class IMSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private static RedisCache redisCache;

    //存储一个channelGroup
    public static final ChannelGroup GROUP = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    public static Map<Long, Channel> USERS = new ConcurrentHashMap<>(1024);


    @Autowired
    public void setRedisCache(RedisCache redisCache) {
        IMSocketHandler.redisCache = redisCache;
    }

    private static final Object removeLocker = new Object();


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("与客户端建立连接，通道开启！");
//        ctx.channel().metadata().
    }


    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {

        //删除长连接
        Long userId = getUserKey(ctx.channel());
        if (userId != null && userId > 0) {
            remove(userId);
            System.out.println("用户"+userId+"离线，当前在线用户列表："+USERS);
        }
        System.out.println("与客户端断开连接，通道关闭！");
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        MessageDto messageDto = JSON.parseObject(msg.text(), MessageDto.class);
        if (SystemConstant.WEB_SOCKET_DATA_TYPE_AUTH.equals(messageDto.getType())){
            String token = (String) messageDto.getData();
            System.out.println(token);
            Claims claims = JwtUtils.parseJWT(token);
            String userId = claims.getSubject();
            LoginUser loginUser = (LoginUser) redisCache.getCacheObject("login:" + userId);
            Long id = Long.valueOf(userId);
            if (loginUser == null){
                throw new SystemException(AppHttpCodeEnum.NEED_LOGIN);
            }
            AttributeKey<Long> key = AttributeKey.valueOf("userId");
            Channel channel = ctx.channel();
            channel.attr(key).set(id);
            if (!USERS.containsKey(id)) {
                USERS.put(id, channel);
                System.out.println("用户"+userId+"上线，当前在线用户列表："+USERS);
            }
        }else if (SystemConstant.WEB_SOCKET_DATA_TYPE_MESSAGE.equals(messageDto.getType())){
            ChatMessageHandler.execute(ctx, msg);
        }
    }

    //异常发生
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }

    /**
     * 获取到每个用户对应的channel
     *
     * @param channel
     * @return
     */
    public Long getUserKey(Channel channel) {
        AttributeKey<Long> key = AttributeKey.valueOf("userId");
        return channel.attr(key).get();
    }

    /**
     * 将用户对应的channel关闭并移除
     *
     * @param userId
     */
    public void remove(Long userId) {
        synchronized (removeLocker) {
            //通过用户的id拿到对应的channel
            Channel channel = USERS.get(userId);
            USERS.remove(userId);
            //关闭channel
            if (!ObjectUtils.isEmpty(channel)) {
                channel.close();
            }
        }
    }
}